package com.wx.spring.ioc.inject;

import com.wx.spring.exception.SpringIocException;
import com.wx.spring.ioc.IocContainer;
import com.wx.spring.ioc.factory.ChildBeanDefinition;
import com.wx.spring.ioc.factory.GenericBeanDefinition;
import com.wx.spring.ioc.parsing.ParsingResources;
import com.wx.spring.ioc.parsing.ParsingResourcesStrategy;
import com.wx.spring.ioc.rules.IocXmlRules;
import com.wx.spring.utils.ConvertUtis;
import com.wx.spring.utils.MyStringUtils;
import com.wx.spring.utils.VesselUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.dom4j.DocumentException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author wx
 * @Description: xml配置注入bean
 * @date 2019/1/15 001518:17
 */
@Slf4j
public  class InjectXmlBean implements InjectBean {

    /**
     * @param contextConfigLocation
     * @description: xml配置注入
     * @author: wx
     * @date: 2019/1/15 0015
     * @return: void
     */
    @Override
    public void injectBeanMap(String contextConfigLocation) throws SpringIocException{
        try {
            ParsingResources strategy = ParsingResourcesStrategy.getStrategy(ParsingResourcesStrategy.keyXmlBean);
            //解析xml bean内容到xmlBeanMap
            strategy.parsingContent(contextConfigLocation);
            Map<String, GenericBeanDefinition> xmlBeanMap = IocContainer.getXmlBeanMap();
            Set<Map.Entry<String, GenericBeanDefinition>> entries = xmlBeanMap.entrySet();
            for (Map.Entry<String, GenericBeanDefinition> entry : entries) {
                String beanId = entry.getKey();
                String className = entry.getValue().getClassName();
                Class aClass = Class.forName(className);
                IocContainer.getBeanMap().put(ConvertUtis.toLowerCaseFirstOne(aClass.getSimpleName()), injectBean(beanId));
            }
        } catch (DocumentException e) {
            e.printStackTrace();
            log.error("Documen解析xml异常");
            throw new RuntimeException("Documen解析xml异常");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            log.error("获取class失败");
            throw new RuntimeException("获取class失败");
        }

    }

    /**
     * @description: 注入bean
     * @author: wx
     * @date: 2019/1/15 0015
     * @param beanId
     * @return: java.lang.Object
     */
    private Object injectBean(String beanId){
        if (MyStringUtils.isBlank(beanId)) {
            throw new NullPointerException("beanId为空");
        }
        Object object = null;
        //获取xml bean中的内容
        GenericBeanDefinition genericBeanDefinition = IocContainer.getXmlBeanMap().get(beanId);
        if (genericBeanDefinition == null) {
            return null;
        }
        String beanName = genericBeanDefinition.getClassName();
        try {
            //获取class
            Class<?> aClass = Class.forName(beanName);
            // 此对象的意思是对象的属性集合
            List<ChildBeanDefinition> childBeanDefinitionList =
                    genericBeanDefinition.getChildBeanDefinitionList();

            if (CollectionUtils.isNotEmpty(childBeanDefinitionList)) {
                List<ChildBeanDefinition> setChildBeanDefinition = new ArrayList<>();
                List<ChildBeanDefinition> constructorChildBeanDefinition = new ArrayList<>();
                for (ChildBeanDefinition childBeanDefinition : childBeanDefinitionList) {
                    //如果是Set注入
                    if (IocXmlRules.SET_INJECT.getType().equals(childBeanDefinition.getType())) {
                        setChildBeanDefinition.add(childBeanDefinition);
                    }
                    //如果是构造器注入
                    else if (IocXmlRules.CONS_INJECT.getType().equals(childBeanDefinition.getType())) {
                        constructorChildBeanDefinition.add(childBeanDefinition);
                    }
                }
                //如果有构造器注入，先执行构造器注入方法再执行set注入方法
                if (CollectionUtils.isNotEmpty(constructorChildBeanDefinition)) {
                    object = consValue(aClass, constructorChildBeanDefinition);
                } else {
                    object = aClass.newInstance();
                }

                if (CollectionUtils.isNotEmpty(setChildBeanDefinition)) {
                    for (ChildBeanDefinition childBeanDefinition : setChildBeanDefinition) {
                        //执行set注入
                        setValue(aClass, childBeanDefinition, object);
                    }
                }
            } else {
                object = aClass.newInstance();
            }
        } catch (ReflectiveOperationException e) {
            log.error("反射实例化对象异常");
            e.printStackTrace();
            throw new SpringIocException("反射实例化对象异常");
        } catch (SpringIocException s) {
            log.error(s.getMessage());
            throw s;
        }


        return object;
    }

    /**
     * @param aClass
     * @param childBeanDefinitionList
     * @description: 构造器注入的方法
     * @author: wx
     * @date: 2019/1/11 0011
     * @return: java.lang.Object
     */
    private Object consValue(Class<?> aClass, List<ChildBeanDefinition> childBeanDefinitionList) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取所有构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        if (VesselUtils.isArrayEmpty(declaredConstructors)) {
            log.error("{}异常,没有发现任何构造器", aClass.getName());
            throw new SpringIocException(String.format("{0}异常,没有发现任何构造器", aClass.getName()));
        }

        //接收匹配的构造器
        Constructor<?> constructor = null;
        //构造器参数数组
        Object[] parameterArray = null;
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            //获取每个构造器的参数列表
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            //如果参数匹配
            if (parameterTypes.length == childBeanDefinitionList.size()) {
                constructor = declaredConstructor;
                parameterArray = new Object[childBeanDefinitionList.size()];
                //设置构造器参数
                for (int i = 0; i < parameterTypes.length; i++) {
                    ChildBeanDefinition childBeanDefinition = childBeanDefinitionList.get(i);
                    parameterArray[Integer.valueOf(childBeanDefinition.getAttributeOne())] = ConvertUtis.convert(parameterTypes[i].getSimpleName(), childBeanDefinition.getAttributeTwo());
                }
                break;
            }
        }

        if (constructor == null) {
            log.error("{}构造器注入方式无匹配的参数列表", aClass.getName());
            throw new SpringIocException(String.format("{0}构造器注入方式无匹配的参数列表", aClass.getName()));
        }
        return constructor.newInstance(parameterArray);
    }


    /**
     * @param aClass
     * @param childBeanDefinition
     * @param object
     * @description: set注入
     * @author: wx
     * @date: 2019/1/11 0011
     * @return: void
     */
    private void setValue(Class<?> aClass, ChildBeanDefinition childBeanDefinition, Object object) throws InvocationTargetException, NoSuchFieldException, IllegalAccessException {
        String propertyName = childBeanDefinition.getAttributeOne();
        String propertyValue = childBeanDefinition.getAttributeTwo();
        // 拼接set方法名
        String methodName = ConvertUtis.getSetMethodNameByField(propertyName);

        //根据属性名 获取属性 无视权限
        Field field = aClass.getDeclaredField(propertyName);
        //属性返回类型简写
        String type = field.getType().getSimpleName();

        //获取所有方法
        Method[] methods = aClass.getMethods();

        for (Method method : methods) {
            //如果匹配到set方法
            if (methodName.equals(method.getName())) {
                Object value = ConvertUtis.convert(type, propertyValue);
                if (value != null) {
                    method.invoke(object, ConvertUtis.convert(type, propertyValue));
                } else {
                    log.error("暂时不支持该属性,{}", type);
                }

            }
        }

    }
}
